package qc.module.iot.api.consumer;

import org.apache.commons.lang3.StringUtils;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import qc.common.core.constants.IotConstant;
import qc.common.core.enums.iot.IotPacketsDataProtocolEnum;
import qc.common.core.pair.KeyValuePairDto;
import qc.common.core.utils.LocalDateTimeUtil;
import qc.common.core.utils.RadixConvertUtil;
import qc.module.iot.api.RestTemplateQms;
import qc.module.iot.dto.MqIotPacketsMessageDto;
import qc.module.iot.protocol.IotFrameBodyDataItem;
import qc.module.iot.protocol.IotFrameBodyDataItemTypeEnum;
import qc.module.iot.protocol.IotFrameParseBase;
import qc.module.iot.protocol.IotFrameParseResult;
import qc.module.iot.protocol.custom.Custom_DeHongSwj_FrameParse;
import qc.module.iot.protocol.modbus.Modbus_Lj_Ems_FrameParse;
import qc.module.iot.protocol.sl427.SL427_FrameParse;
import qc.module.iot.protocol.sl651.SL651_Ascii_FrameParse;
import qc.module.iot.protocol.sl651.SL651_Hex_FrameParse;
import qc.module.iot.service.IotFramePacketsService;
import qc.module.qms.dto.rawdata.QmsRawDataOperateResultDto;
import qc.module.qms.dto.rawdata.QmsRawDataReceiveDto;

import java.time.LocalDateTime;
import java.util.LinkedList;
import java.util.List;

/**
 * Iot数据帧报文解析Consumer
 *
 * @author QuCheng Tech
 * @since 2025/10/28
 */
//@RefreshScope
@Component
@RocketMQMessageListener(
        topic = IotConstant.MQ_FRAME_PACKETS_TOPIC,
        consumerGroup = IotConstant.MQ_FRAME_PACKETS_CONSUMER_PARSE_GROUP
)
public class IotPacketsParseConsumer implements RocketMQListener<MqIotPacketsMessageDto> {
    private static final Logger logger = LoggerFactory.getLogger(IotPacketsParseConsumer.class);

    @Value("${qucheng.consumer.packets-parse:false}")
    private boolean enable;

    //模块名称，用于在日志或显示中统一使用
    private static final String moduleName = "Iot报文消息解析数据帧";

    @Autowired
    IotFramePacketsService framePacketsService;

    @Autowired
    RestTemplateQms restTemplateQms;

    @Override
    public void onMessage(MqIotPacketsMessageDto message) {
        logger.debug("onMessage " + moduleName);
        logger.debug("onMessage " + moduleName + " 当前消费者状态为：" + (enable ? "启用" : "禁用"));

        //不需要对消息进行有效性验证，理论上消息内容都是有效的，均通过IotFramePacketsService中receive方法生成并发送
        if (message != null) {
            //解析报文需要根据报文字节数据和数据协议来解析，此处的数据帧就是分解好并且判断基本结构正确的数据帧
            byte[] bytes = message.getBytes();
            IotPacketsDataProtocolEnum messageDataProtocol = message.getDataProtocol();

            //数据帧解析类，根据数据协议不同进行不同的创建
            IotFrameParseBase dataFrameParse = null;
            if (messageDataProtocol == IotPacketsDataProtocolEnum.SL651_HEX)
                dataFrameParse = new SL651_Hex_FrameParse();
            else if (messageDataProtocol == IotPacketsDataProtocolEnum.SL651_ASCII)
                dataFrameParse = new SL651_Ascii_FrameParse();
            else if (messageDataProtocol == IotPacketsDataProtocolEnum.SL_427)
                dataFrameParse = new SL427_FrameParse();
            else if (messageDataProtocol == IotPacketsDataProtocolEnum.MODBUS_LJ_EMS)
                dataFrameParse = new Modbus_Lj_Ems_FrameParse();
            else if (messageDataProtocol == IotPacketsDataProtocolEnum.CUSTOM_DEHONG_SWJ)
                dataFrameParse = new Custom_DeHongSwj_FrameParse();

            boolean parseSuccess = false;
            StringBuilder sb = new StringBuilder();
            //获取解析结果中的站点编码和时标，如果没有保持为null
            String stcd = null;
            LocalDateTime datatime = null;

            try {
                //判断如果数据协议对应的数据帧解析类为null无法进行解析
                if (dataFrameParse == null) {
                    logger.warn("onMessage " + moduleName + "，解析数据帧失败，未实现的数据协议。记录ID[{}]，数据协议[{}]", message.getId(), messageDataProtocol);
                    parseSuccess = false;
                    sb.append("未实现该数据协议");
                } else {
                    IotFrameParseResult frameParseResult = dataFrameParse.parse(bytes, true);
                    if (frameParseResult.isParseSuccess() == false) {
                        logger.info("onMessage " + moduleName + "，解析数据帧失败，报文[{}]，解析错误信息[{}]", RadixConvertUtil.toHexString(bytes), frameParseResult.getParseErrorMsg());
                        parseSuccess = false;
                        sb.append(frameParseResult.getParseErrorMsg());
                    } else {
                        logger.info("onMessage " + moduleName + "，解析数据帧成功，帧类型[{}]，帧名称[{}]，报文[{}]，完整数据帧报文[{}]", frameParseResult.getFrameType(),
                                frameParseResult.getFrameName(), RadixConvertUtil.toHexString(bytes), RadixConvertUtil.toHexString(frameParseResult.getParseFrameBytes()));

                        parseSuccess = true;
                        //解析结果字符串中添加数据帧的基本信息
                        sb.append("帧类型[" + frameParseResult.getFrameType() + "]");
                        if (StringUtils.isNotBlank(frameParseResult.getFrameName()))
                            sb.append("，帧名称[" + frameParseResult.getFrameName() + "]");
                        if (StringUtils.isNotBlank(frameParseResult.getCenterAddress()))
                            sb.append("，中心站地址[" + frameParseResult.getCenterAddress() + "]");
                        if (StringUtils.isNotBlank(frameParseResult.getStationAddress()))
                            sb.append("，遥测站地址[" + frameParseResult.getStationAddress() + "]");
                        if (StringUtils.isNotBlank(frameParseResult.getPassword()))
                            sb.append("，密码[" + frameParseResult.getPassword() + "]");
                        if (StringUtils.isNotBlank(frameParseResult.getFunctionCode()))
                            sb.append("，功能码[" + frameParseResult.getFunctionCode() + "]");
                        if (frameParseResult.getDataTime() != null)
                            sb.append("，时标[" + LocalDateTimeUtil.formatDateTime(frameParseResult.getDataTime()) + "]");

                        //获取解析结果中的站点编码
                        if (StringUtils.isNotBlank(frameParseResult.getStationAddress()))
                            stcd = frameParseResult.getStationAddress();

                        List<KeyValuePairDto> elements = new LinkedList<>();

                        //解析结果字符串中添加解析出的正文数据
                        List<IotFrameBodyDataItem> bodyDataItems = frameParseResult.getBodyDataItems();
                        if (bodyDataItems != null && bodyDataItems.size() > 0x0) {
                            logger.debug("onMessage " + moduleName + "，解析数据帧成功，共有正文内容项" + bodyDataItems.size() + "个。");
                            int dataIndex = 0x1;
                            for (IotFrameBodyDataItem item : bodyDataItems) {
                                logger.debug("onMessage " + moduleName + "，解析数据帧成功，正文内容项 " + dataIndex + " ，类型：" +
                                        item.getType() + "，名称：" + item.getName() + "，值：" + item.getValue().toString() + "，编码：" + item.getCode());
                                sb.append("，正文内容[" + String.valueOf(dataIndex++) + "]-[" + item.getType() + "][" +
                                        item.getName() + "]~[" + item.getCode() + "]:[" + item.getValue().toString() + "]");

                                //如果数据帧解析结果中没有stcd，正文内容项为遥测站地址时进行获取赋值
                                if (StringUtils.isBlank(stcd) && item.getType() == IotFrameBodyDataItemTypeEnum.STATION_ADDRESS)
                                    stcd = item.getValue().toString();
                                //如果时标为null，正文内容为时标时进行获取赋值
                                if (datatime == null && item.getType() == IotFrameBodyDataItemTypeEnum.TIME)
                                    datatime = (LocalDateTime) item.getValue();

                                //如果正文内容为数据值时放入到采集要素和对应值集合中
                                if (item.getType() == IotFrameBodyDataItemTypeEnum.VALUE) {
                                    //判断数据值不为空，并且是数值类型，要求采集要素是已知/预期的（未知的无法对应数据库表的列名）
                                    if (item.getValue() != null && StringUtils.isNotBlank(item.getValue().toString()) &&
                                            StringUtils.equalsIgnoreCase(item.getValue().toString(), IotConstant.NO_DATA_VALUE_STRING) == false &&
                                            item.isUnKnown() == false) {
                                        KeyValuePairDto keyValuePairDto = new KeyValuePairDto();
                                        keyValuePairDto.setKey(item.getCode());
                                        keyValuePairDto.setValue(item.getValue().toString());

                                        elements.add(keyValuePairDto);
                                    }
                                }
                            }
                        }
                        //如果从数据帧正文内容解析结果中获取不到站号和时标，使用消息中的中的站号和数据时标
                        if (StringUtils.isBlank(stcd) && StringUtils.isNotBlank(message.getStcd()))
                            stcd = message.getStcd();
                        if (datatime == null && message.getIntm() != null)
                            datatime = message.getIntm();

                        //解析成功后调用原始数据保存接口进行原始数据保存
                        //必须调用QMS中的原始数据接收接口，只有在接收接口中才有发送至MQ或保存到数据库功能
                        //站点编码和数据时标必须都有，采集要素和对应值集合也不能为空
                        if (StringUtils.isNotBlank(stcd) && datatime != null && elements.size() > 0x0) {
                            //原始数据接收DTO
                            QmsRawDataReceiveDto rawDataReceiveDto = new QmsRawDataReceiveDto();
                            //实际数据帧使用的协议从解析结果中获取
                            IotPacketsDataProtocolEnum dataProtocol = frameParseResult.getProtocol();
                            //数据库表编码：固定使用+协议编码
                            String tableCode = "qms_rawdata";
                            if (dataProtocol == IotPacketsDataProtocolEnum.SL651_HEX || dataProtocol == IotPacketsDataProtocolEnum.SL651_ASCII)
                                tableCode = "qms_rawdata_sl651";
                            else if (dataProtocol == IotPacketsDataProtocolEnum.SL_427)
                                tableCode = "qms_rawdata_sl427";
                            else if (dataProtocol == IotPacketsDataProtocolEnum.MODBUS_LJ_EMS)
                                tableCode = "qms_rawdata_emslj";
                            else if (dataProtocol == IotPacketsDataProtocolEnum.CUSTOM_DEHONG_SWJ)
                                tableCode = "qms_rawdata_swjlj";

                            rawDataReceiveDto.setTablecode(tableCode);
                            rawDataReceiveDto.setStcd(stcd);
                            rawDataReceiveDto.setTm(LocalDateTimeUtil.formatDateTime(datatime));
                            rawDataReceiveDto.setChannel(message.getChannelType().getIndex());
                            rawDataReceiveDto.setElements(elements);

                            QmsRawDataOperateResultDto receiveRawDataResult = restTemplateQms.receiveRawData(rawDataReceiveDto);
                            if (receiveRawDataResult != null) {
                                if (receiveRawDataResult != null && receiveRawDataResult.isSuccess() == true) {
                                    //保存成功
                                    logger.info("onMessage " + moduleName + "，解析数据帧成功后保存至原始数据库表[" + tableCode + "]成功。");
                                } else {
                                    //保存到数据库失败
                                    logger.error("onMessage " + moduleName + "，解析数据帧成功后保存至原始数据库表[" + tableCode + "]失败，提示信息[" + receiveRawDataResult.getMsg() + "]。");
                                }
                            }
                        }
                    }
                }
            } catch (Exception ex) {
                parseSuccess = false;
                sb.append("解析数据帧异常，" + ex.getMessage());
            }

            //修改数据帧的解析结果
            String setParseResult = framePacketsService.setParseResult(message.getId(), parseSuccess, sb.toString(), stcd, datatime);
            if (StringUtils.isBlank(setParseResult)) {
                //成功
                logger.debug("onMessage " + moduleName + "，修改数据帧解析结果成功，记录ID[{}]，报文[{}]", message.getId(), message.getHex());
            } else {
                //失败
                logger.warn("onMessage " + moduleName + "，修改数据帧解析结果失败，记录ID[{}]，报文[{}]，提示信息[{}]", message.getId(), message.getHex(), setParseResult);
            }
        }
    }
}
